/*
 * Copyright 2011 Dominik Pretzsch <dominik.pretzsch at gmail.com>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.blacksheep.jmoteextract.remote;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Dominik Pretzsch <dominik.pretzsch at gmail.com>
 */
public class RemoteProgressObservable extends UnicastRemoteObject
                                      implements IRemoteObservable {
    
    public static final int REMOTE_SET_BYTESDONE  = 0;
    public static final int REMOTE_SET_BYTESTOTAL = 1;
    public static final int REMOTE_SET_ERRORMSG   = 2; 
     
    protected int hash;
    protected ArrayList<IRemoteObserver> observers = new ArrayList<IRemoteObserver>();
    
    private static final int    WAIT_MAX = 3000;
    private static final Logger LOGGER   = Logger.getLogger(RemoteProgressObservable.class.getName());

    public RemoteProgressObservable() throws RemoteException {
        super();
    }

    public void addObserver(IRemoteObserver ro) throws RemoteException {
        LOGGER.log(Level.FINE, "Adding remote observer");
        observers.add(ro);
    }

    public void removeObserver(IRemoteObserver ro) throws RemoteException {
        LOGGER.log(Level.FINE, "Removing remote observer");
        observers.remove(ro);
    }

    public void removeAllObservers() throws RemoteException {
        LOGGER.log(Level.FINE, "Removing all remote observer");
        observers.clear();
    }

    public void notifySubscribers(int id, Object data) {
        LOGGER.log(Level.FINE, "Try to notifySubscribers");
        ListIterator e = observers.listIterator();
        while (e.hasNext()) {   
            LOGGER.log(Level.FINE, "Found subscriber");
            IRemoteObserver s = (IRemoteObserver) e.next();
            // Because updating may cause a long delay,
            // when connecting or in case of error,
            // it is performed in an UpdateThread
            UpdateThread ut = new UpdateThread(s, id, data);
            //WaitThread wt = new WaitThread(ut);
            LOGGER.log(Level.FINE, "Starting UpdateThread");
            ut.start();
            //LOGGER.log(Level.FINE, "Starting WaitThread");
            //wt.start();
        }
    }
    
    private class WaitThread extends Thread {
        
        private UpdateThread ut;

        public WaitThread(UpdateThread ut) {
            this.ut = ut;        
            this.setName(this.getClass().getName());
            this.setDaemon(true);
        }        
        
        @Override
        public void run() {
            try {
                LOGGER.log(Level.FINE, "Waiting for UpdateThread. long = {0}ms", WAIT_MAX);
                ut.join(WAIT_MAX);
                LOGGER.log(Level.FINE, "Interrupting UpdateThread");
                ut.interrupt();
            } catch (InterruptedException ex) {
                LOGGER.log(Level.SEVERE, null, ex);
            }
        }
    }
    
    private class UpdateThread extends Thread {
        
        private Object data;
        private int id;
        private IRemoteObserver observer;

        public UpdateThread(IRemoteObserver observer, int id, Object data) { 
            this.observer = observer;
            this.id       = id;
            this.data     = data;        
            
            this.setName(this.getClass().getName());
            this.setDaemon(true);
        }
        
        @Override
        public void run() {
            try {
                LOGGER.log(Level.FINE, "Updating remote observer {0}", id);
                observer.update(id, data);
            } catch (java.rmi.ConnectException ce) { //serious 
                LOGGER.log(Level.SEVERE, "ConnectException", ce);
                observers.remove(observer);
            } catch (java.rmi.NoSuchObjectException nsoe) { //serious  
                LOGGER.log(Level.SEVERE, "NoSuchObjectException", nsoe);
                observers.remove(observer);
            } catch (java.rmi.RemoteException re) {
                LOGGER.log(Level.SEVERE, "RemoteException", re);
                /*might recover?*/
            }
        }
    }

    public void notifySubscribers(int id) {
        notifySubscribers(id, null);
    }
}
